package com.chenfan.mcn.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.chenfan.common.exception.BusinessException;
import com.chenfan.mcn.clients.baseinfo.BaseInfoClient;
import com.chenfan.mcn.clients.baseinfo.DescByCodeDTO;
import com.chenfan.mcn.config.UserVoContextHolder;
import com.chenfan.mcn.constant.BillCodeCst;
import com.chenfan.mcn.constant.DictTypeCst;
import com.chenfan.mcn.constant.SeparatorCst;
import com.chenfan.mcn.dao.ContractExcutionOrderMapper;
import com.chenfan.mcn.dao.PurchaseContractMapper;
import com.chenfan.mcn.enums.ApprovalEnum;
import com.chenfan.mcn.enums.ApprovalStatusEnum;
import com.chenfan.mcn.enums.ExcutionApprovalStatusEnum;
import com.chenfan.mcn.exception.McnErrorCode;
import com.chenfan.mcn.extension.Res;
import com.chenfan.common.extension.dict.DictStream;
import com.chenfan.common.extension.dict.DictTranslationUtils;
import com.chenfan.mcn.extension.excel.ExcelUtils;
import com.chenfan.mcn.component.CodeGenerateHelper;
import com.chenfan.mcn.model.ContractExcutionOrder;
import com.chenfan.mcn.model.McnPage;
import com.chenfan.mcn.model.PurchaseContract;
import com.chenfan.mcn.model.StarPlatformInfo;
import com.chenfan.mcn.model.dto.ApprovalFlowDTO;
import com.chenfan.mcn.model.dto.ChooseExecutionDTO;
import com.chenfan.mcn.model.dto.PurchaseContractDTO;
import com.chenfan.mcn.model.dto.PurchaseContractSearchDTO;
import com.chenfan.mcn.model.vo.*;
import com.chenfan.mcn.service.*;
import com.chenfan.mcn.utils.CommonUtil;
import com.google.common.collect.Lists;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import javax.servlet.http.HttpServletResponse;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author lizhejin
 * @since 2021-02-26
 */
@Service
public class PurchaseContractServiceImpl implements PurchaseContractService, ApprovalCallbackService {

    @Autowired
    private PurchaseContractMapper purchaseContractMapper;

    @Autowired
    private ApprovalFlowService approvalFlowService;

    @Autowired
    private ContractExcutionOrderMapper contractExcutionOrderMapper;

    @Autowired
    private BaseInfoClient baseInfoClient;

    @Autowired
    private StarPlatformInfoService starPlatformInfoService;

    @Autowired
    private BrandInfoService brandInfoService;


    /**
     * 新增/编辑采购合同
     *
     * @param purchaseContractDTO {@link PurchaseContractDTO}
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long saveData(PurchaseContractDTO purchaseContractDTO) {
        if (StringUtils.isNotBlank(purchaseContractDTO.getExecutionCode())) {
            List<String> list = JSONObject.parseArray(purchaseContractDTO.getExecutionCode(), String.class);
            Assert.isTrue(list.size() <= 50, "最多只能关联50条执行单");
        }
        PurchaseContract purchaseContract = new PurchaseContract();
        BeanUtils.copyProperties(purchaseContractDTO, purchaseContract);
        if (Objects.isNull(purchaseContract.getId())) {
            purchaseContract.setContractCode(CodeGenerateHelper.getCode(BillCodeCst.PURCHASE_CONTRACT));
            purchaseContractMapper.insert(purchaseContract);
            if (Objects.nonNull(purchaseContract.getPurchaseStatus()) && purchaseContract.getPurchaseStatus() == ApprovalStatusEnum.APPROVAL.code()) {
                approvalFlowService.startProcess(purchaseContract.getId(), purchaseContract.getContractCode(), ApprovalEnum.PURCHASE_CONTRACT, JSON.toJSONString(purchaseContract));
            }
            //执行单关联采购合同
            if (StringUtils.isNotBlank(purchaseContract.getExecutionCode())) {
                executionInsertPurchaseCode(purchaseContract.getContractCode(), JSONObject.parseArray(purchaseContract.getExecutionCode(), String.class));
            }
        } else {
            saveVertifyStatus(purchaseContract.getId());
            PurchaseContract before = purchaseContractMapper.selectById(purchaseContract.getId());
            purchaseContractMapper.updateById(purchaseContract);
            if (purchaseContract.getPurchaseStatus() == ApprovalStatusEnum.APPROVAL.code()) {
                approvalFlowService.startProcess(before.getId(), before.getContractCode(), ApprovalEnum.PURCHASE_CONTRACT, JSON.toJSONString(purchaseContract));
            }
            //执行单关联采购合同(编辑)
            executionUpdatePurchaseCode(before, purchaseContract);
        }
        return purchaseContract.getId();
    }

    private void saveVertifyStatus(Long id) {
        PurchaseContract purchaseContract = purchaseContractMapper.selectById(id);
        if (purchaseContract.getPurchaseStatus() != ApprovalStatusEnum.UN_COMMIT.code() &&
                purchaseContract.getPurchaseStatus() != ApprovalStatusEnum.REJECTED.code() &&
                purchaseContract.getPurchaseStatus() != ApprovalStatusEnum.CANCEL.code()) {
            throw new BusinessException(McnErrorCode.DISCOUNT_APPROVA_UPDATE_ERROR);
        }
    }

    private void executionUpdatePurchaseCode(PurchaseContract before, PurchaseContract purchaseContract) {
        List<String> beforeExecutionCodes = new ArrayList<>();
        List<String> executionCodes = new ArrayList<>();
        String beforeExecutionCode = before.getExecutionCode();
        String executionCode = purchaseContract.getExecutionCode();
        if (StringUtils.isNotBlank(beforeExecutionCode)) {
            beforeExecutionCodes = JSONObject.parseArray(beforeExecutionCode, String.class);
        }
        if (StringUtils.isNotBlank(executionCode)) {
            executionCodes = JSONObject.parseArray(executionCode, String.class);
        }
        List<String> finalExecutionCodes = executionCodes;
        List<String> deleteCodes = beforeExecutionCodes.stream().filter(item -> !finalExecutionCodes.contains(item)).collect(Collectors.toList());
        if (deleteCodes.size() > 0) {
            contractExcutionOrderMapper.batchUpdatePurchaseCode(deleteCodes, null);
        }
        List<String> finalBeforeExecutionCodes = beforeExecutionCodes;
        List<String> insertCodes = executionCodes.stream().filter(item -> !finalBeforeExecutionCodes.contains(item)).collect(Collectors.toList());
        this.executionInsertPurchaseCode(before.getContractCode(), insertCodes);
    }

    private void executionInsertPurchaseCode(String contractCode , List<String> excuteCodes) {
        if(CollectionUtils.isEmpty(excuteCodes)) {
            return;
        }
        McnPage<ChooseExecution> mcnPage = new McnPage<>();
        //不分页
        mcnPage.setPageSize(-1L);
        //不统计总数
        mcnPage.setSearchCount(false);
        ChooseExecutionDTO dto = new ChooseExecutionDTO();
        dto.setExecuteCodes(excuteCodes);
        dto.setTenantId(UserVoContextHolder.getTenantId());
        List<ChooseExecution> dataList = contractExcutionOrderMapper.getExecutionOrders(mcnPage, dto).getRecords();
        if(CollectionUtils.isEmpty(dataList) || excuteCodes.size() > dataList.size()) {
            if(CollectionUtils.isNotEmpty(dataList)) {
                dataList.forEach(e -> {
                    excuteCodes.remove(e.getExcuteCode());
                });
            }
            throw new BusinessException(McnErrorCode.BUSINESS_ERROR.code(), "请剔除已关联采购合同的执行单！编号=" + StringUtils.join(excuteCodes, SeparatorCst.VERTICAL_LINE));
        }
        contractExcutionOrderMapper.batchUpdatePurchaseCode(excuteCodes, contractCode);
    }

    /**
     * 查询指定收入合同下关联的采购合同单号
     *
     * @param incomeContractCode {@link String}
     * @return {@link List}
     */
    @Override
    public List<PurchaseSimpleVO> getPurchaseByContract(String incomeContractCode) {
        return purchaseContractMapper.getPurchaseByContract(incomeContractCode);
    }

    /**
     * 修改采购合同状态
     *
     * @param id     {@link Long}
     * @param status {@link Integer}
     * @return {@link Boolean}
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean changeStatus(Long id, Integer status) {
        PurchaseContract purchaseContract = purchaseContractMapper.selectById(id);
        if (Objects.isNull(purchaseContract)) {
            throw new BusinessException(McnErrorCode.PURCHASE_NOT_EXISTS);
        }
        validateStatus(purchaseContract, status);
        purchaseContract.setPurchaseStatus(status);
        boolean result = purchaseContractMapper.updateById(purchaseContract) > 0;
        if (result) {
            if (status == ApprovalStatusEnum.APPROVAL.code()) {
                approvalFlowService.startProcess(id, purchaseContract.getContractCode(), ApprovalEnum.PURCHASE_CONTRACT, JSON.toJSONString(purchaseContract));
            }
            if (status == ApprovalStatusEnum.CANCEL.code() || status == ApprovalStatusEnum.INVALID.code()) {
                if (status == ApprovalStatusEnum.INVALID.code()) {
                    if (StringUtils.isNotBlank(purchaseContract.getExecutionCode())) {
                        List<String> list = JSONObject.parseArray(purchaseContract.getExecutionCode(), String.class);
                        List<ContractExcutionOrder> executionOrderList = contractExcutionOrderMapper.selectList(Wrappers.lambdaQuery(ContractExcutionOrder.class)
                                .eq(ContractExcutionOrder::getIsDelete, Boolean.FALSE)
                                .in(ContractExcutionOrder::getExcuteCode, list));
                        List<Integer> statusList = executionOrderList.stream().map(ContractExcutionOrder::getApprovalStatus).distinct().collect(Collectors.toList());
                        boolean contains = statusList.contains(ExcutionApprovalStatusEnum.APPROVAL.getCode()) || statusList.contains(ExcutionApprovalStatusEnum.PASSED.getCode()) || statusList.contains(ExcutionApprovalStatusEnum.PAID.getCode());
                        if (contains) {
                            throw new BusinessException(McnErrorCode.PURCHASE_VOID_FAILED);
                        }
                        // 解除关联关系
                        contractExcutionOrderMapper.batchUpdatePurchaseCode(list, null);
                    }
                }
                approvalFlowService.revokeProcess(id, ApprovalEnum.PURCHASE_CONTRACT.getProcessId());
            }
        }
        return result;
    }


    /**
     * 详情
     *
     * @param page 分页
     * @param id   {@link Long}
     * @return {@link Res <PurchaseContractVO>}
     */
    @Override
    public PurchaseContractVO info(McnPage<PurchaseContractVO> page, Long id) {
        PurchaseContractVO purchaseContract = purchaseContractMapper.info(id, ApprovalEnum.PURCHASE_CONTRACT.getProcessId());
        if (Objects.isNull(purchaseContract)) {
            throw new BusinessException(McnErrorCode.PARAM_ERROR);
        }
        List<Long> flowList = new ArrayList<>();
        if (Objects.nonNull(purchaseContract.getFlowId())) {
            flowList = Collections.singletonList(purchaseContract.getFlowId());
        }
        JSONObject flowMapping = approvalFlowService.getFlowStatus(flowList);
        if (Objects.nonNull(flowMapping)) {
            if (flowMapping.size() > 0 && Objects.nonNull(purchaseContract.getFlowId())) {
                purchaseContract.setFlowStatusName(flowMapping.getString(purchaseContract.getFlowId().toString()));
            }
        }
        String executionCode = purchaseContract.getExecutionCode();
        if (StringUtils.isNotBlank(executionCode)) {
            List<String> list = JSONObject.parseArray(executionCode, String.class);
            Page<ChooseExecution> iPage = contractExcutionOrderMapper.getPurchaseInfo(page, list);
            Page<ChooseExecution> executionPage = dictChooseExecution(iPage);
            purchaseContract.setChooseExecutionIpage(executionPage);
        }
        return purchaseContract;
    }

    /**
     * 分页数据
     *
     * @param page                      {@link McnPage}
     * @param searchDTO {@link PurchaseContractSearchDTO}
     * @return
     */
    @Override
    public Page<PurchaseVO> getList(McnPage<PurchaseVO> page, PurchaseContractSearchDTO searchDTO) {
        searchDTO.setProcessId(ApprovalEnum.PURCHASE_CONTRACT.getProcessId());
        searchDTO.setAlias("pc.");
        if (Objects.nonNull(searchDTO.getApprovalStatus())) {
            searchDTO.setSrcIds(approvalFlowService.getSrcList(Lists.newArrayList(searchDTO.getApprovalStatus()), searchDTO.getProcessId()));
        }
        Page<PurchaseVO> list = purchaseContractMapper.getList(page, searchDTO);
        List<Long> flowList = list.getRecords().stream().map(PurchaseVO::getFlowId).filter(Objects::nonNull).collect(Collectors.toList());
        JSONObject flowMapping = null;
        if (flowList.size() > 0) {
            flowMapping = approvalFlowService.getFlowStatus(flowList);
        }
        if (Objects.nonNull(flowMapping)) {
            JSONObject finalFlowMapping = flowMapping;
            list.getRecords().forEach(vo -> {
                if (finalFlowMapping.size() > 0 && Objects.nonNull(vo.getFlowId())) {
                    vo.setFlowStatusName(finalFlowMapping.getString(vo.getFlowId().toString()));
                }
            });
        }
        return list;
    }

    /**
     * 采购合同列表导出
     *
     * @param searchDTO {@link PurchaseContractSearchDTO}
     */
    @Override
    public void exportExcel(PurchaseContractSearchDTO searchDTO, HttpServletResponse response) throws Exception {
        searchDTO.setProcessId(ApprovalEnum.PURCHASE_CONTRACT.getProcessId());
        searchDTO.setAlias("pc.");
        if (Objects.nonNull(searchDTO.getApprovalStatus())) {
            searchDTO.setSrcIds(approvalFlowService.getSrcList(Lists.newArrayList(searchDTO.getApprovalStatus()), searchDTO.getProcessId()));
        }
        List<PurchaseVO> list = purchaseContractMapper.exportExcel(searchDTO);
        List<Long> flowList = list.stream().map(PurchaseVO::getFlowId).filter(Objects::nonNull).collect(Collectors.toList());
        JSONObject flowMapping = null;
        if (flowList.size() > 0) {
            flowMapping = approvalFlowService.getFlowStatus(flowList);
        }
        if (Objects.nonNull(flowMapping)) {
            JSONObject finalFlowMapping = flowMapping;
            list.forEach(vo -> {
                if (finalFlowMapping.size() > 0 && Objects.nonNull(vo.getFlowId())) {
                    vo.setFlowStatusName(finalFlowMapping.getString(vo.getFlowId().toString()));
                }
            });
        }
        ExcelUtils.exportExcel(list, "采购合同", PurchaseVO.class, "采购合同导出.xlsx", response);
    }

    @Override
    public PurchaseContract getByCode(String contractCode) {
        return purchaseContractMapper.selectOne(Wrappers.lambdaQuery(PurchaseContract.class)
                .eq(PurchaseContract::getContractCode, contractCode)
                .eq(PurchaseContract::getIsDelete, Boolean.FALSE));
    }

    @Override
    public Page<ChooseExecution> getExecutionOrders(McnPage<ChooseExecution> page, ChooseExecutionDTO executionDTO) {
        Page<ChooseExecution> iPage = contractExcutionOrderMapper.getExecutionOrders(page, executionDTO);
        return dictChooseExecution(iPage);
    }

    @Override
    public void infoExport(Long id, HttpServletResponse response) throws Exception {
        PurchaseContract purchaseContract = purchaseContractMapper.selectById(id);
        String executionCode = purchaseContract.getExecutionCode();
        McnPage<PurchaseContractVO> page = new McnPage<>();
        page.setPageNum(1L).setPageSize(99999L);
        if (StringUtils.isNotBlank(executionCode)) {
            List<String> list = JSONObject.parseArray(executionCode, String.class);
            Page<ChooseExecution> purchaseInfo = contractExcutionOrderMapper.getPurchaseInfo(page, list);
            Page<ChooseExecution> iPage = dictChooseExecution(purchaseInfo);
            ExcelUtils.exportExcel(iPage.getRecords(), "采购合同执行单", ChooseExecution.class, "采购合同执行单导出.xlsx", response);
        }
        ExcelUtils.exportExcel(new ArrayList<>(), "采购合同执行单", ChooseExecution.class, "采购合同执行单导出.xlsx", response);
    }


    public Page<ChooseExecution> dictChooseExecution(Page<ChooseExecution> iPage) {
        if (CollectionUtils.isNotEmpty(iPage.getRecords())) {
            //查询平台名称
            List<Long> starPlatInfoIds = iPage.getRecords().stream().map(ChooseExecution::getStarPlatformInfoId).collect(Collectors.toList());
            Map<Long, StarPlatformInfo> starPlatformInfoMap = CommonUtil.defaultList(starPlatformInfoService.selectBatchIds(starPlatInfoIds))
                    .stream().collect(Collectors.toMap(StarPlatformInfo::getId, Function.identity()));
            List<String> brandCodes = iPage.getRecords().stream().map(ChooseExecution::getBrandName).filter(Objects::nonNull).distinct().collect(Collectors.toList());
            Map<String,String> brandMap = brandInfoService.getBrandMapByCodes(brandCodes);
            //字典翻译
            DictTranslationUtils.translation(() -> DictStream.of(DictStream.map(DictTypeCst.RELEASE_TYPE, ChooseExecution::getPublishType, ChooseExecution::setPublishType)
                    ), (dictProfileList, dictList) -> baseInfoClient.getDescByCode(new DescByCodeDTO(dictProfileList, dictList)).getObj(), iPage.getRecords());
            Map<String, Map<String, String>> secondlevelDictMap = baseInfoClient.getAllSecondlevelDictMap(DictTypeCst.PUBLISHING_PLATFORM);
            iPage.getRecords().forEach(x -> {
                if (starPlatformInfoMap.containsKey(x.getStarPlatformInfoId())) {
                    StarPlatformInfo starPlatformInfo = starPlatformInfoMap.get(x.getStarPlatformInfoId());
                    x.setPlatformId(starPlatformInfo.getPlatformId());
                    x.setPlatformName(starPlatformInfo.getPlatformName());
                    x.setPlatNickName(starPlatformInfo.getPlatNickName());
                    if (StringUtils.isNotBlank(x.getPublishForm())) {
                        x.setPublishForm(CommonUtil.getMapValue(secondlevelDictMap.get(x.getPlatformId()), x.getPublishForm()));
                    }
                }
                if(brandMap.containsKey(x.getBrandName())) {
                    x.setBrandNameN(brandMap.get(x.getBrandName()));
                }
            });
        }
        return iPage;
    }


    /**
     * 判断审批状态
     *
     * @param purchaseContract {@link PurchaseContract}
     * @param status           {@link Integer}
     */
    @SuppressWarnings("AlibabaAvoidComplexCondition")
    private void validateStatus(PurchaseContract purchaseContract, Integer status) {
        if (purchaseContract.getPurchaseStatus().equals(ApprovalStatusEnum.INVALID.code())) {
            throw new BusinessException(McnErrorCode.PURCHASE_INVALID);
        }
        if (status.equals(ApprovalStatusEnum.PASSED.code()) || status.equals(ApprovalStatusEnum.REJECTED.code())) {
            if (purchaseContract.getPurchaseStatus().equals(ApprovalStatusEnum.CANCEL.code())) {
                throw new BusinessException(McnErrorCode.PURCHASE_CANCELED);
            }
        }
        if (purchaseContract.getPurchaseStatus() == ApprovalStatusEnum.UN_COMMIT.getCode() &&
                (status != ApprovalStatusEnum.APPROVAL.getCode() && status != ApprovalStatusEnum.INVALID.getCode())) {
            throw new BusinessException(McnErrorCode.DISCOUNT_APPROVAL_UN_COMMIT_COMMIT_ERROR);
        }
        if (purchaseContract.getPurchaseStatus() == ApprovalStatusEnum.APPROVAL.getCode() &&
                (status != ApprovalStatusEnum.CANCEL.getCode() && status != ApprovalStatusEnum.INVALID.getCode())) {
            throw new BusinessException(McnErrorCode.DISCOUNT_APPROVAL_UN_COMMIT_CANCEL_ERROR);
        }
        if (purchaseContract.getPurchaseStatus() == ApprovalStatusEnum.PASSED.getCode() &&
                (status != ApprovalStatusEnum.INVALID.getCode())) {
            throw new BusinessException(McnErrorCode.DISCOUNT_APPROVA_PASSED_ERROR);
        }
        if (purchaseContract.getPurchaseStatus() == ApprovalStatusEnum.REJECTED.getCode() &&
                (status != ApprovalStatusEnum.APPROVAL.getCode() && status != ApprovalStatusEnum.INVALID.getCode())) {
            throw new BusinessException(McnErrorCode.DISCOUNT_APPROVAL_REJECTED_ERROR);
        }
        if (purchaseContract.getPurchaseStatus() == ApprovalStatusEnum.CANCEL.getCode() &&
                (status != ApprovalStatusEnum.APPROVAL.getCode() && status != ApprovalStatusEnum.INVALID.getCode())) {
            throw new BusinessException(McnErrorCode.DISCOUNT_APPROVAL_CANCEL_ERROR);
        }
    }


    /**
     * 审批回调
     *
     * @param approvalFlowDTO 流程对对象
     * @param status          最后节点的同意 false 拒绝
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void approvalCallback(ApprovalFlowDTO approvalFlowDTO, Boolean status) {
        PurchaseContract purchaseContract = purchaseContractMapper.selectById(approvalFlowDTO.getSrcId());
        approvalFlowDTO.setSrcCode(purchaseContract.getContractCode());
        approvalFlowDTO.addSubmitInfoIfNull(purchaseContract);
        if (Objects.isNull(status)) {
            //审批流配置-接入待办
            return;
        }
        purchaseContract.setPurchaseStatus(status ? ApprovalStatusEnum.PASSED.code() : ApprovalStatusEnum.REJECTED.code());
        purchaseContract.setUpdateBy(approvalFlowDTO.getUserId());
        purchaseContract.setUpdateName(approvalFlowDTO.getUserName());
        purchaseContract.setUpdateDate(new Date());
        purchaseContractMapper.updateById(purchaseContract);
        approvalFlowService.sendNotify(approvalFlowDTO, purchaseContract.getId(), purchaseContract.getContractCode()
                , ApprovalEnum.PURCHASE_CONTRACT, status, purchaseContract.getCreateBy(), purchaseContract.getCreateName());
    }
}
